﻿using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Diagnostics;

namespace SL.WcfExceptionHandling
{
    public class SilverlightFaultBehavior : BehaviorExtensionElement, IDispatchMessageInspector, IErrorHandler, IServiceBehavior
    {
        #region BehaviorExtensionElement implementation
        public override Type BehaviorType
        {
            get { return typeof(SilverlightFaultBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new SilverlightFaultBehavior();
        }
        #endregion BehaviorExtensionElement implementation

        #region IDispatchMessageInspector implementation
        object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Do nothing to the incoming message.
            return null;
        }

        void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
        {
            if (reply.IsFault)
            {
                HttpResponseMessageProperty property = new HttpResponseMessageProperty();

                // Here the response code is changed to 200.
                property.StatusCode = System.Net.HttpStatusCode.OK;

                reply.Properties[HttpResponseMessageProperty.Name] = property;
            }
        }
        #endregion IDispatchMessageInspector implementation

        #region IErrorHandler implementation
        /// <summary>
        /// Handle all WCF Exceptions
        /// </summary>
        /// <param name="error"></param>
        /// <returns></returns>
        bool IErrorHandler.HandleError(Exception error)
        {
            // Add logging of exception here!
            Debug.WriteLine(error.ToString());
            // return true means we handled the error.
            return true;
        }

        void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            // Add code here to build faultreason for client based on exception
            FaultReason faultReason = new FaultReason(error.Message);
            ExceptionDetail exceptionDetail = new ExceptionDetail(error);

            // For security reasons you can also decide to not give the ExceptionDetail back to the client or change the message, etc
            FaultException<ExceptionDetail> faultException = new FaultException<ExceptionDetail>(exceptionDetail, faultReason, FaultCode.CreateSenderFaultCode(new FaultCode("0")));

            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, faultException.Action);
        }
        #endregion IErrorHandler implementation

        #region IServiceBehavior implementation
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                channelDispatcher.ErrorHandlers.Add(this);

                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
        #endregion IServiceBehavior implementation
    }
}
